package com.wangzhixuan.commons.intercept;

import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.wangzhixuan.commons.sqlparser.SelectVisitorImpl;

import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.select.Select;

@Intercepts({
        @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class, Integer.class})
})
public class TestInterceptor implements Interceptor {
    // 控制器本来就是单例，这样似乎更加合理
    protected Logger logger = LogManager.getLogger(getClass());

    private CCJSqlParserManager parserManager = new CCJSqlParserManager();

    @Override
    public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        if(invocation.getTarget() instanceof StatementHandler) {
            //逻辑代码区
            StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
            // 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过下面的两次循环
            // 可以分离出最原始的的目标类)
            while (metaStatementHandler.hasGetter("h")) {
                Object object = metaStatementHandler.getValue("h");
                metaStatementHandler = SystemMetaObject.forObject(object);
                //logger.error("刘洋拦截器：h");
            }
            // 分离最后一个代理对象的目标类
            while (metaStatementHandler.hasGetter("target")) {
                Object object = metaStatementHandler.getValue("target");
                metaStatementHandler = SystemMetaObject.forObject(object);
                //logger.error("刘洋拦截器：target");
            }
            MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
            //获取sql
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            if(sql.contains("NEXTVAL")){ // 去掉子序列函数
            	return invocation.proceed();
            }
            //获取方法id
			String id = mappedStatement.getId();
			if (id.contains("InventoryInoutMapper") || id.contains("selectSaleReportByGmtCreate")
					|| id.contains("selectBiShipReportByGmtCreate") || id.contains("selectTransNoinventoryReport")
					|| id.contains("queryPurStoDetails") || id.contains("queryTaskDailyCloseIds")
					|| id.contains("selectSaleItemReportByMouth") || id.contains("selectSaleItemReportByProbability")
					|| id.contains("selectSaleItemReportByHistory") || id.contains("selectSaleReportExcelByGmtCreate")
					|| id.contains("selectBiPurchasingSuccessRateByMouth") || id.contains("selectBiPurchasingSuccessRateByWeek")
					|| id.contains("selectBiPurchasingBuysiteSuccessByMouth") || id.contains("selectBiPurchasingBuysiteSuccessByWeek")
					|| id.contains("selectBiShipReportExcelByGmtCreate") || id.contains("selectBiShipDeliveryReportBySalesName")
					|| id.contains("selectBiSellerName") || id.contains("queryByErpOrderId")
					|| id.contains("selectBiBuyerPurchaseSuccess") || id.contains("selectPxImproperReport")
					|| id.contains("selectSaleHeroicListByTime") || id.contains("selectDeliverHeroicListByTime")
					|| id.contains("selectRecordHeroicListByTime") || id.contains("selectPackHeroicListByTime")
					|| id.contains("selectBiShipDeliveryReportBySalesNameMouth")) { // 剔除不需要走拦截器的方法
				return invocation.proceed();
			}
            //获得方法类型
            SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
            //logger.error("刘洋拦截器：" + sqlCommandType);
            if ("SELECT".equals(sqlCommandType.toString())) {
            	//logger.error("刘洋拦截器：拦截");
            	//如果是select就将sql转成SELECT对象  
            	try {
            		Select select = (Select)parserManager.parse(new StringReader(sql));
            		//访问各个visitor  
                    select.getSelectBody().accept(new SelectVisitorImpl());  
                    //将增强后的sql放回
                    metaStatementHandler.setValue("delegate.boundSql.sql", select.toString());
                    return invocation.proceed();
            	} catch(Throwable e) {
            		logger.error("拦截器异常Start");
            		logger.error(e);
            		logger.error(id);
            		logger.error("拦截器异常End");
            	}
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        //生成代理对象
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
